package model

import (
	"orm/internal/errs"
	"reflect"
	"strings"
	"sync"
	"unicode"
)

const (
	tagKeyColumn = "column"
)

type Option func(m *Model) error

type Model struct {
	TableName string
	Fields    []*Field
	// go 字段名到 字段定义的映射
	FieldsMap map[string]*Field
	// 列名 到字段定义的映射
	ColumnMap map[string]*Field
}

type Field struct {
	// 结构体名
	GoName string
	// 类型
	Type reflect.Type
	// 列名
	ColName string

	// 字段相对于结构体本身的偏移量
	Offset uintptr
}

type Registry interface {
	Get(val any) (*Model, error)
	Registry(entity any, opts ...Option) (*Model, error)
}

// registry 代表的是元数据的注册中心
type registry struct {
	//lock   sync.RWMutex
	//models map[reflect.Type]*model
	models sync.Map
}

func NewRegistry() Registry {
	return &registry{
		//models: make(map[reflect.Type]*Model, 64),
	}
}

func (r *registry) Get(val any) (*Model, error) {
	typ := reflect.TypeOf(val)
	m, ok := r.models.Load(typ)
	if ok {
		return m.(*Model), nil
	}
	m, err := r.Registry(val)
	if err != nil {
		return nil, err
	}

	return m.(*Model), err
}

//func (r *registry) get1(val any) (*Model, error) {
//	Type := reflect.TypeOf(val)
//	r.lock.RLock()
//	// double check
//	m, ok := r.models[Type]
//	r.lock.RUnlock()
//	if ok {
//		return m, nil
//	}
//	r.lock.Lock()
//	defer r.lock.Unlock()
//	// double check
//	m, ok = r.models[Type]
//	if ok {
//		return m, nil
//	}
//	m, err := r.parseModel(val)
//	if err != nil {
//		return nil, err
//	}
//	r.models[Type] = m
//	return m, nil
//}

// 限制只能用一级指针
func (r *registry) Registry(entity any, opts ...Option) (*Model, error) {
	typ := reflect.TypeOf(entity)
	if typ.Kind() != reflect.Ptr || typ.Elem().Kind() != reflect.Struct {
		return nil, errs.ErrPointerOnly
	}
	elemTyp := typ.Elem()
	numField := elemTyp.NumField()
	fieldMap := make(map[string]*Field, numField)
	columnMap := make(map[string]*Field, numField)
	fields := make([]*Field, 0, numField)
	for i := 0; i < numField; i++ {
		fd := elemTyp.Field(i)
		pair, err := r.parseTag(fd.Tag)
		if err != nil {
			return nil, err
		}
		columnName := pair[tagKeyColumn]
		if columnName == "" {
			// 用戶沒有設置
			columnName = underscoreName(fd.Name)
		}

		fdMeta := &Field{
			ColName: columnName,
			Type:    fd.Type,
			GoName:  fd.Name,
			Offset:  fd.Offset,
		}

		columnMap[columnName] = fdMeta
		fieldMap[fd.Name] = fdMeta
		fields = append(fields, fdMeta)
	}

	var tableName string
	if tbl, ok := entity.(TableName); ok {
		tableName = tbl.TableName()
	}
	if tableName == "" {
		tableName = underscoreName(elemTyp.Name())
	}
	res := &Model{
		TableName: tableName,
		FieldsMap: fieldMap,
		ColumnMap: columnMap,
		Fields:    fields,
	}
	for _, opt := range opts {
		if err := opt(res); err != nil {
			return nil, err
		}
	}
	r.models.Store(typ, res)
	return res, nil
}

func WithTableName(tablename string) Option {
	return func(m *Model) error {
		m.TableName = tablename
		//if tablename == ""{
		//	return errs.
		//}
		return nil
	}
}

func WithColumnName(field string, colName string) Option {
	return func(m *Model) error {
		fd, ok := m.FieldsMap[field]
		if !ok {
			return errs.NewErrUnknownField(field)
		}
		fd.ColName = colName
		return nil
	}
}

func (r *registry) parseTag(tag reflect.StructTag) (map[string]string, error) {
	ormTag, ok := tag.Lookup("orm")
	if !ok {
		return map[string]string{}, nil
	}
	pairs := strings.Split(ormTag, ",")
	res := make(map[string]string, len(pairs))
	for _, pair := range pairs {
		segs := strings.Split(pair, "=")
		if len(segs) != 2 {
			return nil, errs.NewErrInvalidTagContent(pair)
		}
		res[segs[0]] = segs[1]
	}
	return res, nil
}

// underscoreName 驼峰转字符串命名
func underscoreName(tableName string) string {
	var buf []byte
	for i, v := range tableName {
		if unicode.IsUpper(v) {
			if i != 0 {
				buf = append(buf, '_')
			}
			buf = append(buf, byte(unicode.ToLower(v)))
		} else {
			buf = append(buf, byte(v))
		}

	}
	return string(buf)
}

type TableName interface {
	TableName() string
}
